package br.com.transfred.dao.framework;

import br.com.transfred.exception.ConnectionFailureDaoException;
import br.com.transfred.exception.FileNotFoundException;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

public class DaoHelper {

    private static final ThreadLocal<Connection> context = new ThreadLocal<Connection>();
    private static String driver;
    private static String url;
    private static String usuario;
    private static String senha;

    /**
     * Fornecer conexao ao banco de dados
     *
     * @return Connection
     * @throws SQLExceptione
     */
    public Connection getConnection() throws SQLException {
        Properties properties = new Properties();
        FileInputStream arquivoDePropriedades = null;
        Connection conn = null;
        try {
            arquivoDePropriedades = new FileInputStream("database.properties");
            properties.load(arquivoDePropriedades);
            driver = properties.getProperty("jdbc.driver");
            url = properties.getProperty("jdbc.url");
            usuario = properties.getProperty("jdbc.username");
            senha = properties.getProperty("jdbc.password");
            Class.forName(driver);
            conn = DriverManager.getConnection(url, usuario, senha);
        } catch (ClassNotFoundException e) {
            throw new ConnectionFailureDaoException("Não foi possível realizar a conexao", e);
        } catch (java.io.FileNotFoundException e) {
            throw new FileNotFoundException("Não foi possível localizar o arquivo database.properties", e);
        } catch (IOException e) {
            throw new FileNotFoundException("Não foi possível lêr o arquivo database.properties", e);
        }
        return conn;

    }

    public void beginTransaction() throws SQLException {
        if (isTransactionStarted()) {
            return; // << 13 -- provide support to transaction propagation
        }
        Connection conn = getConnection();
        conn.setAutoCommit(false);
        context.set(conn);
    }

    private boolean isTransactionStarted() {
        return (context.get() != null);
    }

    public void endTransaction() throws SQLException {
        commit(getConnectionFromContext());
        releaseTransaction();
    }

    public void releaseTransaction() throws SQLException {
        Connection conn = getConnectionFromContext();
        release(conn);
        context.remove();
    }

    public void rollbackTransaction() {
        Connection conn;
        try {
            conn = getConnectionFromContext();
            rollback(conn);
            release(conn);
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        context.remove();
    }

    public void commit(Connection conn) throws SQLException {
        conn.commit();
    }

    public void rollback(Connection conn) throws SQLException {
        if (conn != null) {
            conn.rollback();
        }
    }

    public Connection getConnectionFromContext() throws SQLException {

        Connection conn = context.get();

        if (conn == null) {
            throw new SQLException("Transação invalida");
        }

        if (conn.isClosed()) {
            throw new SQLException(
                    "Transaçãoo invalida, Conexao esta fechada :Z");
        }

        return conn;

    }

    public long executePreparedUpdateAndReturnGeneratedKeys(Connection conn,
            String query, Object... params)
            throws SQLException {

        PreparedStatement pstmt = null;
        ResultSet rset = null;
        long result = 01;
        try {
            pstmt = conn.prepareStatement(query,
                    PreparedStatement.RETURN_GENERATED_KEYS);
            int i = 0;
            for (Object param : params) {
                pstmt.setObject(++i, param);
            }

            pstmt.executeUpdate();

            rset = pstmt.getGeneratedKeys();

            if (rset.next()) {
                result = rset.getLong(1);
            }

        } finally {
            release(pstmt);
            release(rset);
        }
        return result;
    }

    public void executePreparedUpdate(Connection conn, String query,
            Object... params) throws SQLException {
        PreparedStatement pstmt = null;
        try {
            pstmt = conn.prepareStatement(query);
            int i = 0;
            for (Object param : params) {
                pstmt.setObject(++i, param);
            }
            pstmt.executeUpdate();
        } finally {
            release(pstmt);
        }
    }

    public void executePreparedUpdateIntoSingleTransaction(String query,
            Object... params)
            throws SQLException {
        executePreparedUpdate(getConnection(), query, params);
    }

    public void executePreparedUpdate(String query,
            Object... params)
            throws SQLException {

        executePreparedUpdate(getConnectionFromContext(), query, params);
    }

    public <T> List<T> executePreparedQuery(String query,
            QueryMapper<T> mapper)
            throws SQLException {

        Connection conn = null;
        Statement stmt = null;
        ResultSet rset = null;

        List<T> list = new ArrayList<T>();

        try {
            conn = getConnection();
            stmt = conn.createStatement();
            rset = stmt.executeQuery(query);
            list = mapper.mapping(rset);
        } finally {
            releaseAll(conn, stmt, rset);
        }

        return list;

    }

    public void release(Statement stmt) {
        if (stmt == null) {
            return;
        }
        try {
            stmt.close();
        } catch (SQLException e) {
        }
    }

    public void release(Connection conn) {

        if (conn == null) {
            return;
        }
        try {
            conn.close();
        } catch (SQLException e) {
        }

    }

    public void release(ResultSet rset) {
        if (rset == null) {
            return;
        }
        try {
            rset.close();
        } catch (SQLException e) {
        }
    }

    public void releaseAll(Connection conn, Statement stmt) {
        release(stmt);
        release(conn);
    }

    public void releaseAll(Connection conn, Statement stmt, ResultSet rset) {
        release(rset);
        releaseAll(conn, stmt);
    }
}
